/*
 * Grupo:		Danilo Beraldo Ireno
 * 				Flavio Augusto de Mello
 * 				Giuliano G. Maranha
 * 				Luis Fernando Junior
 *
 * Disciplina: 	Compiladores
 * Professor:	Dr. Eraldo Pereira Marinho*
 */
#include <string.h>

#include "parser.h"
#include "tokens.h"
#include "keywords.h"
#include "lexer.h"
#include "asmgen.h"
#include "symtab.h"


int glabel = 1;
int lexlevel = 0;
char lastSignal[MAXLEN];

/**
 * typespec -> INTEGER | REAL | TEXT | CHAR | BOOLEAN | ID
 */

void typespec(void)
{
    switch (lookahead) {
    case INTEGER:
        match(INTEGER);
        break;
    case REAL:
        match(REAL);
        break;
    case TEXT:
    	match(TEXT);
        break;
    case CHAR:
        match(CHAR);
        break;
    case BOOLEAN:
    	match(BOOLEAN);
    	break;
    default: match(ID);/* check on symtab, if this id IS a type identifier*/
    }
}

/**
 * unsignedNUM -> UINT ['.' UINT] [E SIGNAL UINT]
 */

void unsignedNUM(void){

	semantic_asm(UINTASM);
	match(UINT);
	if(lookahead == '.'){

		match('.');
		match(UINT);
	}

	if(lookahead == 'E'){
		match('E');
		match(SIGNAL);
		match(UINT);
	}
}

/**
 * unsignedConst -> ID | UINT | CHARCONST
 */

void unsignedConst(void){

	switch(lookahead){

		case ID: match(ID);
		break;
		case UINT: unsignedNUM();
		break;
		default:match(CHARCONST);
	}
}

/**
 * cons -> CHARCONST | [SIGNAL ID | SIGNAL unsignedNum]
 */

void cons(void){

	switch (lookahead){

		case CHARCONST: match(CHARCONST);
		break;
		default:{

			if(lookahead == SIGNAL){
				match(SIGNAL);
			}
			switch(lookahead){

				case ID: match(ID);
				break;
				default: unsignedNUM();
			}
		}
	}
}

/**
 * sympleType -> typespec() | (ID {','ID}) | cons DOTDOT cons
 */

void sympleType(void){

	switch(lookahead){

		case INTEGER:
	    case REAL:
	    case TEXT:
	    case CHAR:
	    case BOOLEAN: typespec();
		break;
		case '(':{
			match('(');
			match(ID);
			while(lookahead == ','){
				match(',');
				match(ID);
			}
			match(')');
		}
		break;
		default: cons();match(DOTDOT);cons();
	}
}

/**
 * type -> PFILE OF type | ARRAY '[' sympleType {','sympleType} ']' OF type
 */

void type(void){

	switch(lookahead){

		case PFILE: match(PFILE);match(OF);type();
		break;
		case ARRAY: match(ARRAY);match('['); sympleType(); while(lookahead == ','){match(',');sympleType();}match(']'); match(OF);type();
		break;
		default: sympleType();
	}
}

/**
 * variable -> {'[' expr {',' expr} ']' | '^'}
 */

void variable(void){

	while(lookahead == '[' || lookahead == '^'){

		switch(lookahead){

			case '[': match('[');expr();while(lookahead == ','){match(',');expr();} match(']');
			break;
			default: match('^');//apontador
		}
	}
}

/**
 * factor -> ID [(expr {',' expr})] [variable] | (expr) | NOT factor | '[' [expr {',' expr}] ']'
 */

void factor(void){

	/*
	 * unsigned const, variable and ident function can begin with ID
	 */

	switch(lookahead){

		case ID:{

			match(ID);
			if(lookahead == '('){
				match('(');
				expr();
				while(lookahead == ','){
					match(',');
					expr();
				}
				match(')');
			}
			/* variable without match ID*/
			if(lookahead == '[' || lookahead == '^'){

				variable();
			}
		}
		break;
		case '(': match('(');expr();match(')');
		break;
		case NOT: match(NOT); factor();
		break;
		case '[': {

			match('[');

			if(lookahead == ']'){
				match(']');
			}else{
				expr();
				while(lookahead == ','){

					match(',');
					expr();
				}
				match(']');
			}
		}
		break;
		/* unsignedConst without ID */
		default: {

			switch(lookahead){

					case UINT: unsignedNUM();
					break;
					case TRUE: match(TRUE);
					break;
					case FALSE: match(FALSE);
					break;
					default:match(CHARCONST);
				}
		}
	}
}

/**
 * term -> factor {factor}
 */

void term(void){

	factor();

	int mullAction;

	while(mullAction = ismull(lookahead)){
		factor();semantic_asm(mullAction);
	}
}

/**
 * simpleExpr -> [SIGNAL] term {term}
 */

void simpleExpr(void){

	if(lookahead == SIGNAL){
		match(SIGNAL);
	}

	term();
	while(isadd(lookahead)){
		term();(strcmp(lastSignal,"+") == 0)?semantic_asm(ADDASM):semantic_asm(SUBASM);
	}
}

/**
 * expr -> simpleExpr {simpleExpr}
 */

void expr(void){

	simpleExpr();

	int relopAction;

	while( relopAction = isrelop(lookahead)){
		simpleExpr();semantic_asm(relopAction);
	}
}

/**
 * paramList -> ( PROCEDURE ID {',' ID} {; PROCEDURE ID {',' ID}} | paramListAux {';' PROCEDURE ID {',' ID}} )
 */

void paramList(void){

	if(lookahead == '('){

		match('(');

		switch(lookahead){

			case PROCEDURE: match(PROCEDURE); match(ID); while(lookahead == ','){match(','),match(ID);}
			break;
			default:paramListAux();
		}

		while(lookahead == ';'){

			match(';');

			switch(lookahead){

						case PROCEDURE: match(PROCEDURE); match(ID); while(lookahead == ','){match(','),match(ID);}
						break;
						default:paramListAux();
			}
		}

		match(')');
	}

}

/**
 * paramListAux -> [VAR | FUNCTION] ID {','ID} ':' typespec
 */

void paramListAux(void){

	if(lookahead == VAR || lookahead == FUNCTION){
		match(lookahead);
	}

	match(ID);
	while(lookahead == ','){
		match(',');
		match(ID);
	}
	match(':');
	typespec();
}

/**
 * command -> [UINT ':'] ( BEGIN command {';' command} END | IF ifstmt | CASE casestmt | WHILE whilestmt | REPEAT repeatstmt | FOR forstmt | GOTO gotostmt | ID ('(' expr {','expr}')' | variable ASSGNM expr | ASGNM expr)
 */

void command(void){

	if(lookahead == UINT){
		match(UINT);
		match(':');
	}

	switch (lookahead) {
		case BEGIN: match(BEGIN); lexlevel++; command(); while(lookahead == ';'){match(';');command();} match(END); lexlevel--;
		break;
		case IF: ifstmt();
		break;
		case CASE: casestmt();
		break;
		case WHILE: whilestmt();
		break;
		case REPEAT: repeatstmt();
		break;
		case FOR: forstmt ();
		break;
		case GOTO: gotostmt();
		break;
		default: {

			match(ID);
				switch(lookahead){
			                       case '(':
			                                        match('('); expr(); while (lookahead ==','){ match(','); expr();} match(')');
			                       break;
			                       case '[':
			                       case '^': variable(); match(ASGNM); expr();
			                       break;
			                       case ASGNM: match(ASGNM);expr();
			                       break;
			               }
			}
	}
}

/**
 * gotostmt -> GOTO UINT
 */

void gotostmt(){

	match(GOTO);

	match(UINT);

}

/**
 * repeatstmt -> REPEAT command {';' command} UNTIL expr
 */

void repeatstmt(){

	int labelinit = glabel++;
	int labelend = glabel++;

	match(REPEAT);
	{fprintf(p_File,".L%d\n",labelinit);}
	command();
	while(lookahead == ';')	{
		match(';');
		command();
	}
	match(UNTIL);
	expr();
	{fprintf(p_File,"\tgofalse .L%d\n",labelend);}
	{fprintf(p_File,"\tgoto .L%d\n",labelinit);}
	{fprintf(p_File,".L%d\n",labelend);}

}

/**
 * forstmt -> FOR ID ASGNM expr toDownTo expr DO command
 */

void forstmt (){

	int labelinit = glabel++;
	int labelend = glabel++;

	match(FOR);
	{fprintf(p_File,".L%d\n",labelinit);}
	{fprintf(p_File,"\tgofalse .L%d\n",labelend);}
	match(ID);
	match(ASGNM);
	expr();
	toDownTo();
	expr();
	match(DO);
	command();
	{fprintf(p_File,"\tgoto .L%d\n",labelinit);}
	{fprintf(p_File,".L%d\n",labelend);}
}

/**
 * IF expr THEN command [ELSE command]
 */

void ifstmt(void){

	int label;

	match(IF);
	expr();
	{fprintf(p_File,"\tgofalse .L%d\n",label = glabel++);}
	match(THEN);
	command();

	if(lookahead == ELSE){
		{fprintf(p_File,"\tgoto .L%d\n",glabel);
		 fprintf(p_File,".L%d:\n",label);label = glabel++;}
		match(ELSE);
		command();
	}
	{fprintf(p_File,".L%d:\n",label);}
}

/**
 * whilestmt -> WHILE expr DO command
 */

void whilestmt(void){

	int label;

	match(WHILE);
	{fprintf(p_File,".L%d:\n",label = glabel++);}
	expr();
	{fprintf(p_File,"\tgofalse .L%d\n",glabel);}
	match(DO);
	command();
	{fprintf(p_File,"\tgoto .LW%d\n",label);}
	{fprintf(p_File,".L%d:\n",glabel);}
}

/**
 * casestmt -> CASE expr OF cons {',' cons} ':' command {';', cons {',' cons}} ':' command END
 */

void casestmt(){

	int endLabel = glabel++;
	int label = glabel;

	match(CASE);
	expr();
	match(OF);
	cons();
	while(lookahead == ','){
		match(',');
		cons();
	}
	match(':');
	{fprintf(p_File,"\tgofalse .L%d\n",glabel);}
	command();
	{fprintf(p_File,"\tgoto .L%d\n",endLabel);}
	while(lookahead == ';'){
		match(';');
		cons();
		while(lookahead == ','){
			match(',');
			cons();
		}
		match(':');
		{fprintf(p_File,".L%d:\n",glabel++);}
		{fprintf(p_File,"\tgofalse .L%d\n",label = glabel++);}
		command();
		{fprintf(p_File,"\tgoto .L%d\n",endLabel);}
	}
	{fprintf(p_File,".L%d:\n",label);}
	{fprintf(p_File,".L%d:\n",endLabel);}
	match(END);
}

/**
 * block -> CONST constBlockLine | TYPE typeBlockLine | VAR varBlockLine | PROCEDURE procedureBlockLine | FUNCTION functionBlockLine | uintBlockLine | BEGIN command {';', command} END
 */

void block(void){

	while(lookahead != BEGIN){

		switch(lookahead){

			case CONST: constBlockLine();
			break;
			case TYPE: typeBlockLine();
			break;
			case VAR: varBlockLine();
			break;
			case PROCEDURE: procedureBlockLine();
			break;
			case FUNCTION: functionBlockLine();
			break;
			default: uintBlockLine();
		}
	}

	match(BEGIN);
	semantic_asm(BSSASM);
	semantic_asm(DATASM);
	semantic_asm(TEXASM);
	semantic_asm(STARTASM);
	lexlevel++;
	command();
	while(lookahead == ';'){
		match(';');
		command();
	}
	match(END);
	lexlevel--;
}

/**
 * block '.'
 */

void program(void){

	block();
	match('.');
}

/*
 * Block Aux Functions
 */

/**
 * uintBlockLine -> UINT {',' UINT} ';'
 */

void uintBlockLine(void){

	match(UINT);
	while(lookahead == ','){
		match(UINT);
	}
	match(';');
}

/**
 * constBlockLine -> CONST ID '=' cons ';' {ID '=' cons ';'}
 */

void constBlockLine(void){

	match(CONST);
	symtab_insert(lexlevel,ISCONST,lexeme);
	match(ID);
	match('=');
	cons();
	match(';');
	while(lookahead == ID){
		symtab_insert(lexlevel,ISCONST,lexeme);
		match(ID);
		match('=');
		cons();
		match(';');
	}
}

/**
 * typeBlockLine -> TYPE ID '=' type ';' {ID '=' type ';'}
 */

void typeBlockLine(void){

	match(TYPE);
	symtab_insert(lexlevel,ISUSERTYPE,lexeme);
	match(ID);
	match('=');
	type();
	match(';');
	while(lookahead == ID){
		symtab_insert(lexlevel,ISUSERTYPE,lexeme);
		match(ID);
		match('=');
		type();
		match(';');
	}
}

/**
 * varBlockLine -> VAR ID {','ID} ':' type ';' {ID {',' ID} ':' type ';'}
 */

void varBlockLine(void){

	match(VAR);
	symtab_insert(lexlevel,ISID,lexeme);
	match(ID);
	while(lookahead == ','){
		match(',');
		symtab_insert(lexlevel,ISID,lexeme);
		match(ID);
	}
	match(':');
	type();
	match(';');

	while(lookahead == ID){
		symtab_insert(lexlevel,ISID,lexeme);
		match(ID);
			while(lookahead == ','){
				match(',');
				symtab_insert(lexlevel,ISID,lexeme);
				match(ID);
			}
			match(':');
			type();
			match(';');
	}
}

/**
 * procedureBlockLine -> PROCEDURE ID paramList ';' block ';'
 */

void procedureBlockLine(void){

	match(PROCEDURE);
	symtab_insert(lexlevel,ISPROCEDURE,lexeme);
	match(ID);
	paramList();
	match(';');
	block();
	match(';');
}

/**
 * functionBlockLine -> FUNCTION ID paramList ':' typespec ';' block ';'
 */

void functionBlockLine(void){

	match(FUNCTION);
	symtab_insert(lexlevel,ISFUNCTION,lexeme);
	match(ID);
	paramList();
	match(':');
	/*match(ID);*/
	typespec();
	match(';');
	block();
	match(';');
}

/**
 * toDownTo -> TO | DOWNTO
 */

void toDownTo(void){

	switch(lookahead){

		case TO: match(TO);
		break;
		default: match(DOWNTO);
	}
}

/**
 * isrelop -> '>' | '<' | '=' | GEQ | LEQ | NEQ | IN
 */

isrelop(int op){

	switch(op){

		case '>': match('>');return GREATASM;
		break;
		case '<': match('<');return LESSASM;
		break;
		case '=': match('=');return EQASM;
		break;
		case GEQ: match(GEQ);return GEQASM;
		break;
		case LEQ: match(LEQ);return LEQASM;
		break;
		case NEQ: match(NEQ);return NEQ;
		break;
		case IN: match(IN);return INASM;
		break;
		default: return 0;
	}
}

/**
 * isadd-> SIGNAL | OR
 */

isadd(int op){

	switch(op){

		case SIGNAL: strcpy(lastSignal,lexeme); match(SIGNAL); return 1;
		break;
		case OR: match(OR); semantic_asm(ORASM);return 1;
		break;
		default: return 0;
	}
}

/**
 * ismull -> '*' | DIV | MOD | '/' | AND
 */

ismull(int op){

	switch(op){

		case '*': match('*');return MULASM;
		break;
		case DIV: match(DIV);return DIVASM;
		break;
		case MOD: match(MOD);return MODASM;
		break;
		case '/': match('/');return DIVISASM;
		break;
		case AND: match(AND);return AND;
		break;
		default: return 0;
	}
}

